<?php

class template{

	var $templateDir		='';
	var $cacheDir			='';
	var $compliedDir		='';

	private $var			=array();
	private $cacheing		=false;
	private $live			='3600';
	private $our			='';
	private $foreachmark	='';
	private $patchstack		='';
	private $foreach		=array();
	private $item			=array();



	function assign($key,$value){

		$this->var[$key]=$value;

	}

	function isCache($filename,$cacheId='')
	{
		$cachename = basename($filename, strrchr($filename, '.')) . '_' . $cacheId;
		$tempFile=$this->cacheDir.$cachename.'.php';
		$config=$this->cacheDir.$cachename.'.config.php';

		if(is_file($tempFile)&&is_file($config))
		{
			$para     = include ($config);
			if ($para === false || time() > $para['expires'])
			{
				$this->cacheing=false;
				return false;
			}
			foreach ($para['template'] AS $val)
			{
				$stat = @stat($val);
				if ($para['maketime'] < $stat['mtime'])
				{
					$this->cacheing=false;
					return false;
				}
			}
		}
		else
		{
			$this->cacheing=false;
			return false;
		}
		$this->cacheing=true;
		return true;
	}

	function display($filename,$cacheId='')
	{
		if(!empty($cacheId)){
			$cachename = basename($filename, strrchr($filename, '.')) . '_' . $cacheId;
			if(!$this->cacheing){
				$this->compiled($filename,$cacheId);
			}
			$tempFile=$this->cacheDir.$cachename.'.php';
			include $tempFile;
		}else
		{
			$fstat = @stat($this->templateDir.$filename);
			$compliedname=basename($filename, strrchr($filename, '.'));
			$compliedFile=$this->compliedDir.$compliedname.'.php';
			$stat = @stat($compliedFile);
			if($stat['mtime']<=time()-$this->live||$fstat['mtime']>$stat['mtime']){
				$this->compiled($filename);
			}
			$source=file_get_contents($compliedFile);
			echo $this->cmd($source);
		}
	}

	function compiled($filename,$cacheId='')
	{
		if(!empty($cacheId)){
			$cachename = basename($filename, strrchr($filename, '.')) . '_' . $cacheId;
			$tempFile=$this->cacheDir.$cachename.'.php';
			$configFile=$this->cacheDir.$cachename.'.config.php';
			$template=$this->templateDir.$filename;
			$out=$this->fetch($template);
			file_put_contents($tempFile, $out);
			$time=time();
			$expires=$time+$this->live;
			foreach($this->item as $r)
			{
				$templates.="'{$r}'";
			}
			$config="<?php return array('expires'=>{$expires},'maketime'=>{$time},'template'=>array({$templates}))?>";
			file_put_contents($configFile, $config);
		}else{
			$template=$this->templateDir.$filename;
			$compliedname=basename($filename, strrchr($filename, '.'));
			$compliedFile=$this->compliedDir.$compliedname.'.php';
			$out=$this->fetchStr(file_get_contents($template));
			file_put_contents($compliedFile, $out);
		}
	}

	function fetch($filename)
	{
		$this->item[]=$filename;
		$out=$this->fetchStr(file_get_contents($filename));
		$out=$this->cmd($out);
		return $out;
	}

	function fetchStr($source)
	{
		$source=preg_replace('/\<\!\-\-\{(.*?)\}\-\-\>/', '\\1', $source);
		$source=preg_replace("/{([^\}\{\n]*)}/e", "\$this->select('\\1');", $source);
		return $source;
	}

	function select($tag)
	{
		$tag = stripslashes(trim($tag));
		if (empty($tag))
		{
			return '{}';
		}
		elseif ($tag{0} == '*' && substr($tag, -1) == '*') // 注释部分
		{
			return '';
		}
		elseif ($tag{0} == '$') // 变量
		{
			return '<?php echo ' . $this->get_val(substr($tag, 1)) . '; ?>';
		}
		elseif ($tag{0} == '/') // 结束 tag
		{
			switch (substr($tag, 1))
			{
				case 'if':
					return '<?php } ?>';
					break;
				case 'foreach':
					if($this->foreachmark=='foreachelse'){
						return '<?php } ?>';
					}else{
						array_pop($this->patchstack);
						return '<?php }} ?>';
					}
					break;
				case 'literal':
					return '';
					break;
				default:
					return '{'. $tag .'}';
					break;
			}
		}
		else
		{
			$tag_sel = array_shift(explode(' ', $tag));
			switch ($tag_sel)
			{
				case 'if':
					return $this->pareIf(substr($tag, 3));
					break;
				case 'else':
					return '<?php }else{ ?>';
					break;
				case 'elseif':
					return $this->pareIf(substr($tag, 7),true);
					break;
				case 'foreachelse':
					$this->foreachmark='foreachelse';
					return '<?php }}else{ ?>';
					break;
				case 'foreach':
					$this->foreachmark = 'foreach';
					if(!isset($this->patchstack))
					{
						$this->patchstack = array();
					}
					return $this->pareForeach(substr($tag, 8));
					break;
				case 'assign':
					$t = $this->get_para(substr($tag, 7));
					if(isset($t['value'])){
						if ($t['value']{0} == '$')
						{
							$tmp = '$this->assign(\'' . $t['var'] . '\',' . $t['value'] . ');';
						}
						else
						{
							$tmp = '$this->assign(\'' . $t['var'] . '\',\'' . addcslashes($t['value'], "'") . '\');';
						}
						return '<?php ' . $tmp . ' ?>';

					}elseif(isset($t['source'])){
						$this->getSource($t['source']);
					}
					break;
				case 'include':
					$t = $this->get_para(substr($tag, 8));
					return '<?php echo $this->fetch(' . "'$t[file]'" . '); ?>';
					break;
				case 'include_static':
					$t = $this->get_para(substr($tag, 15));
					return '<?php echo $this->includeStatic(' . $this->make_array($t) . '); ?>';
					break;
				case 'create_pages':
					$t = $this->get_para(substr($tag, 13));
					return '<?php echo $this->createPages(' . $this->make_array($t) . '); ?>';
					break;
				case 'literal':
					return '';
					break;

				case 'html_options':
					$t = $this->get_para(substr($tag, 13), 0);
					return '<?php echo $this->html_options(' . $this->make_array($t) . '); ?>';
					break;

				case 'html_select_date':
					$t = $this->get_para(substr($tag, 17), 0);
					return '<?php echo $this->html_select_date(' . $this->make_array($t) . '); ?>';
					break;

				case 'html_radios':
					$t = $this->get_para(substr($tag, 12), 0);
					return '<?php echo $this->html_radios(' . $this->make_array($t) . '); ?>';
					break;

				case 'html_select_time':
					$t = $this->get_para(substr($tag, 12), 0);
					return '<?php echo $this->html_select_time(' . $this->make_array($t) . '); ?>';
					break;

				default:
					return '{' . $tag . '}';
					break;
			}
		}
	}

	function get_val($val)
	{

		if (strrpos($val, '[') !== false)
		{
			$val = preg_replace("/\[([^\[\]]*)\]/eis", "'.'.str_replace('$','\$','\\1')", $val);
		}

		if (strrpos($val, '|') !== false)
		{
			$moddb = explode('|', $val);
			$val = array_shift($moddb);
		}

		if (empty($val))
		{
			return '';
		}

		if (strpos($val, '.$') !== false)
		{
			$all = explode('.$', $val);

			foreach ($all AS $key => $val)
			{
				$all[$key] = $key == 0 ? $this->make_var($val) : '['. $this->make_var($val) . ']';
			}
			$p = implode('', $all);
		}
		else
		{
			$p = $this->make_var($val);
		}

		if (!empty($moddb))
		{
			foreach ($moddb AS $key => $mod)
			{
				$s = explode(':', $mod);
				switch ($s[0])
				{
					case 'escape':
						$s[1] = trim($s[1], '"');
						if ($s[1] == 'html')
						{
							$p = 'htmlspecialchars(' . $p . ')';
						}
						elseif ($s[1] == 'url')
						{
							$p = 'urlencode(' . $p . ')';
						}
						elseif ($s[1] == 'decode_url')
						{
							$p = 'urldecode(' . $p . ')';
						}
						elseif ($s[1] == 'quotes')
						{
							$p = 'addslashes(' . $p . ')';
						}
						elseif ($s[1] == 'u8_url')
						{
							$p = 'urlencode(' . $p . ')';
						}
						else
						{
							$p = 'htmlspecialchars(' . $p . ')';
						}
						break;

					case 'nl2br':
						$p = 'nl2br(' . $p . ')';
						break;

					case 'default':
						$s[1] = $s[1]{0} == '$' ?  $this->get_val(substr($s[1], 1)) : "'$s[1]'";
						$p = 'empty(' . $p . ') ? ' . $s[1] . ' : ' . $p;
						break;

					case 'truncate':
						$p = 'sub_str(' . $p . ",$s[1])";
						break;

					case 'strip_tags':
						$p = 'strip_tags(' . $p . ')';
						break;

					default:
						# code...
						break;
				}
			}
		}

		return $p;
	}

	function make_var($val)
	{
		if (strrpos($val, '.') === false)
		{
			if (isset($this->var[$val]) && isset($this->patchstack[$val]))
			{
				$val = $this->patchstack[$val];
			}
			$p = '$this->var[\'' . $val . '\']';
		}
		else
		{
			$t = explode('.', $val);
			$_var_name = array_shift($t);
			if (isset($this->var[$_var_name]) && isset($this->patchstack[$_var_name]))
			{
				$_var_name = $this->patchstack[$_var_name];
			}
			if ($_var_name == 'smarty')
			{
				$p = $this->compile_smarty($t);
			}
			else
			{
				$p = '$this->var[\'' . $_var_name . '\']';
			}
			foreach ($t AS $val)
			{
				$p.= '[\'' . $val . '\']';
			}
		}

		return $p;
	}

	function get_para($val)
	{
		/* 处理'a=b c=d k = f '类字符串，返回数组 */
		while (strpos($val, '= ') != 0)
		{
			$val = str_replace('= ', '=', $val);
		}
		while (strpos($val, ' =') != 0)
		{
			$val = str_replace(' =', '=', $val);
		}
		$pa=explode(' ', trim($val));
		foreach ($pa AS $value)
		{
			if (strrpos($value, '='))
			{
				list($a, $b) = explode('=', str_replace(array(' ', '"', "'", '&quot;'), '', $value));
				if ($b{0} == '$')
				{
					$para[$a] = $this->get_val(substr($b, 1));
				}
				else
				{
					$para[$a] = $b;
				}
			}
		}

		return $para;
	}

	function pareIf($tag_args,$elseif=false)
	{
		preg_match_all('/\-?\d+[\.\d]+|\'[^\'|\s]*\'|"[^"|\s]*"|[\$\w\.]+|!==|===|==|!=|<>|<<|>>|<=|>=|&&|\|\||\(|\)|,|\!|\^|=|&|<|>|~|\||\%|\+|\-|\/|\*|\@|\S/', $tag_args, $match);

		$tokens = $match[0];
		// make sure we have balanced parenthesis
		$token_count = array_count_values($tokens);
		if (!empty($token_count['(']) && $token_count['('] != $token_count[')'])
		{
			// $this->_syntax_error('unbalanced parenthesis in if statement', E_USER_ERROR, __FILE__, __LINE__);
		}

		for ($i = 0, $count = count($tokens); $i < $count; $i++)
		{
			$token = &$tokens[$i];
			switch (strtolower($token))
			{
				case 'eq':
					$token = '==';
					break;

				case 'ne':
				case 'neq':
					$token = '!=';
					break;

				case 'lt':
					$token = '<';
					break;

				case 'le':
				case 'lte':
					$token = '<=';
					break;

				case 'gt':
					$token = '>';
					break;

				case 'ge':
				case 'gte':
					$token = '>=';
					break;

				case 'and':
					$token = '&&';
					break;

				case 'or':
					$token = '||';
					break;

				case 'not':
					$token = '!';
					break;

				case 'mod':
					$token = '%';
					break;

				default:
					if ($token[0] == '$')
					{
						$token = $this->get_val(substr($token, 1));
					}
					break;
			}
		}

		if ($elseif)
		{
			return '<?php }elseif (' . implode(' ', $tokens) . '){ ?>';
		}
		else
		{
			return '<?php if (' . implode(' ', $tokens) . '){ ?>';
		}
	}

	function pareForeach($tag_args)
	{
		$attrs = $this->get_para($tag_args, 0);
		$arg_list = array();
		$from = $attrs['from']?$attrs['from']:$this->getSource($attrs['source']);
		if(isset($this->var[$attrs['item']]) && !isset($this->patchstack[$attrs['item']]))
		{
			$this->patchstack[$attrs['item']] = $attrs['item'] . '_' . str_replace(array(' ', '.'), '_', microtime());
			$attrs['item'] = $this->patchstack[$attrs['item']];
		}
		else
		{
			$this->patchstack[$attrs['item']] = $attrs['item'];
		}
		$item = $this->get_val($attrs['item']);

		if (!empty($attrs['key']))
		{
			$key = $attrs['key'];
			$key_part = $this->get_val($key).' => ';
		}
		else
		{
			$key = null;
			$key_part = '';
		}

		if (!empty($attrs['name']))
		{
			$name = $attrs['name'];
		}
		else
		{
			$name = null;
		}

		$output = '<?php ';
		$output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); };";

		if (!empty($name))
		{
			$foreach_props = "\$this->foreach['$name']";
			$output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
			$output .= "if ({$foreach_props}['total'] > 0){\n";
			$output .= "    foreach (\$_from AS $key_part$item){\n";
			$output .= "        {$foreach_props}['iteration']++;\n";
		}
		else
		{
			$output .= "if (count(\$_from)){\n";
			$output .= "    foreach (\$_from AS $key_part$item){\n";
		}
		return $output . '?>';
	}

	function compile_smarty($indexes)
	{
		$_ref = $indexes[0];

		switch ($_ref)
		{
			case 'now':
				$compiled_ref = 'time()';
				break;

			case 'foreach':
				array_shift($indexes);
				$_var = $indexes[0];
				$_propname = $indexes[1];
				switch ($_propname)
				{
					case 'index':
						array_shift($indexes);
						$compiled_ref = "(\$this->foreach['$_var']['iteration'] - 1)";
						break;

					case 'first':
						array_shift($indexes);
						$compiled_ref = "(\$this->foreach['$_var']['iteration'] <= 1)";
						break;

					case 'last':
						array_shift($indexes);
						$compiled_ref = "(\$this->foreach['$_var']['iteration'] == \$this->foreach['$_var']['total'])";
						break;

					case 'show':
						array_shift($indexes);
						$compiled_ref = "(\$this->foreach['$_var']['total'] > 0)";
						break;
							
					case 'number':
						array_shift($indexes);
						$compiled_ref = "(\$this->foreach['$_var']['iteration'])";
						break;

					default:
						$compiled_ref = "\$this->foreach['$_var']";
						break;
				}
				break;

			case 'get':
				$compiled_ref = '$_GET';
				break;

			case 'post':
				$compiled_ref = '$_POST';
				break;

			case 'cookies':
				$compiled_ref = '$_COOKIE';
				break;

			case 'env':
				$compiled_ref = '$_ENV';
				break;

			case 'server':
				$compiled_ref = '$_SERVER';
				break;

			case 'request':
				$compiled_ref = '$_REQUEST';
				break;

			case 'session':
				$compiled_ref = '$_SESSION';
				break;

			default:
				break;
		}
		array_shift($indexes);

		return $compiled_ref;
	}

	function html_options($arr)
	{
		$selected = $arr['selected'];

		if ($arr['options'])
		{
			$options = (array)$arr['options'];
		}
		elseif ($arr['output'])
		{
			if ($arr['values'])
			{
				foreach ($arr['output'] AS $key => $val)
				{
					$options["{$arr[values][$key]}"] = $val;
				}
			}
			else
			{
				$options = array_values((array)$arr['output']);
			}
		}
		if ($options)
		{
			foreach ($options AS $key => $val)
			{
				$out .= $key == $selected ? "<option value=\"$key\" selected>$val</option>" : "<option value=\"$key\">$val</option>";
			}
		}

		return $out;
	}

	function html_select_date($arr)
	{
		$pre = $arr['prefix'];
		if (isset($arr['time']))
		{
			if (intval($arr['time']) > 10000)
			{
				$arr['time'] = gmdate('Y-m-d', $arr['time'] + 8*3600);
			}
			$t     = explode('-', $arr['time']);
			$year  = strval($t[0]);
			$month = strval($t[1]);
			$day   = strval($t[2]);
		}
		$now = gmdate('Y', $this->_nowtime);
		if (isset($arr['start_year']))
		{
			if (abs($arr['start_year']) == $arr['start_year'])
			{
				$startyear = $arr['start_year'];
			}
			else
			{
				$startyear = $arr['start_year'] + $now;
			}
		}
		else
		{
			$startyear = $now - 3;
		}

		if (isset($arr['end_year']))
		{
			if (strlen(abs($arr['end_year'])) == strlen($arr['end_year']))
			{
				$endyear = $arr['end_year'];
			}
			else
			{
				$endyear = $arr['end_year'] + $now;
			}
		}
		else
		{
			$endyear = $now + 3;
		}

		$out = "<select name=\"{$pre}Year\">";
		for ($i = $startyear; $i <= $endyear; $i++)
		{
			$out .= $i == $year ? "<option value=\"$i\" selected>$i</option>" : "<option value=\"$i\">$i</option>";
		}
		if ($arr['display_months'] != 'false')
		{
			$out .= "</select>&nbsp;<select name=\"{$pre}Month\">";
			for ($i = 1; $i <= 12; $i++)
			{
				$out .= $i == $month ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
			}
		}
		if ($arr['display_days'] != 'false')
		{
			$out .= "</select>&nbsp;<select name=\"{$pre}Day\">";
			for ($i = 1; $i <= 31; $i++)
			{
				$out .= $i == $day ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
			}
		}

		return $out . '</select>';
	}

	function html_radios($arr)
	{
		$name    = $arr['name'];
		$checked = $arr['checked'];
		$options = $arr['options'];

		$out = '';
		foreach ($options AS $key => $val)
		{
			$out .= $key == $checked ? "<input type=\"radio\" name=\"$name\" value=\"$key\" checked>&nbsp;{$val}&nbsp;"
			: "<input type=\"radio\" name=\"$name\" value=\"$key\">&nbsp;{$val}&nbsp;";
		}

		return $out;
	}

	function html_select_time($arr)
	{
		$pre = $arr['prefix'];
		if (isset($arr['time']))
		{
			$arr['time'] = gmdate('H-i-s', $arr['time'] + 8*3600);
			$t     = explode('-', $arr['time']);
			$hour  = strval($t[0]);
			$minute = strval($t[1]);
			$second   = strval($t[2]);
		}
		$out = '';
		if (!isset($arr['display_hours']))
		{
			$out .= "<select name=\"{$pre}Hour\">";
			for ($i = 0; $i <= 23; $i++)
			{
				$out .= $i == $hour ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
			}

			$out .= "</select>&nbsp;";
		}
		if (!isset($arr['display_minutes']))
		{
			$out .= "<select name=\"{$pre}Minute\">";
			for ($i = 0; $i <= 59; $i++)
			{
				$out .= $i == $minute ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
			}

			$out .= "</select>&nbsp;";
		}
		if (!isset($arr['display_seconds']))
		{
			$out .= "<select name=\"{$pre}Second\">";
			for ($i = 0; $i <= 59; $i++)
			{
				$out .= $i == $second ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">$i</option>";
			}

			$out .= "</select>&nbsp;";
		}

		return $out;
	}

	function make_array($arr)
	{
		$out = '';
		foreach ($arr AS $key => $val)
		{
			if ($val{0} == '$')
			{
				$out .= $out ? ",'$key'=>$val" : "array('$key'=>$val";
			}
			else
			{
				$out .= $out ? ",'$key'=>'$val'" : "array('$key'=>'$val'";
			}
		}

		return $out . ')';
	}

	function smarty_create_pages($params)
	{
		extract($params);

		if (empty($page))
		{
			$page = 1;
		}

		if (!empty($count))
		{
			$str = "<option value='1'>1</option>";
			$min = min($count - 1, $page + 3);
			for ($i = $page - 3 ; $i <= $min ; $i++)
			{
				if ($i < 2)
				{
					continue;
				}
				$str .= "<option value='$i'";
				$str .= $page == $i ? " selected='true'" : '';
				$str .= ">$i</option>";
			}
			if ($count > 1)
			{
				$str .= "<option value='$count'";
				$str .= $page == $count ? " selected='true'" : '';
				$str .= ">$count</option>";
			}
		}
		else
		{
			$str = '';
		}

		return $str;
	}

	function getSource($url)
	{
		$data=json_decode(file_get_contents($url),true);
		$basename=basename($url, strrchr($url, '.'));
		$this->var[$basename]=$data;
		return '$this->var[\'' . $basename . '\']';
	}

	function cmd($content)
	{
		ob_start();
		eval('?' . '>' . trim($content));
		$content = ob_get_contents();
		ob_end_clean();

		return $content;

	}


}


?>